/* 试着写写 这里是岛的调度器 */
/**
 * 岛的任务
 * 
 */

type promiseAbort = Promise<any> & { abort?: Function }
type workState = 0 | 1 | 2 | 3 // 0 未执行 1 执行中 2 执行完成 3 过期
class landWork {
    private wid: string
    private duration: number
    private expiration: number
    private name: string
    private priority: number
    private handle: Function
    public shiftTime: number
    public state: workState
    public sleepTimer: promiseAbort | undefined
    readonly work: Record<string, any>
    /**
     * 
     * @param name 任务名 string
     * @param priority 优先级 number 0为立即执行 不允许和duration都是0
     * @param handle 任务 Function
     * @param duration 持续时间 number 可选 默认1500
     * @param expiration 过期时间 number 0为立即过期 NAN为不过期 可选 默认 0
     */
    constructor(name: string, priority: number, handle: Function, duration?: number, expiration?: number) {
        this.duration = duration || 1.5e3
        this.expiration = expiration || 0
        this.name = name
        this.priority = priority
        this.handle = handle
        this.wid = Math.random().toString().slice(2, 6)
        this.state = 0
        this.shiftTime = 0
        this.work = { wid: this.wid, duration: this.duration, expiration: this.expiration, name: this.name, priority: this.priority, handle: this.handle, shiftTime: this.shiftTime }
    }

    getWork() {
        return this.work
    }

    expirationLess(num: number) {
        this.expiration = this.expiration - num
    }
    setShiftTime(shiftTime: number) {
        this.shiftTime = shiftTime
    }
}

class landCtrl {
    queue: Array<landWork>
    timer: number
    activeWork: promiseAbort | null
    queueState: 0 | 1
    constructor() {
        this.queue = []
        this.timer = 0
        this.activeWork = null
        this.queueState = 0
    }
    /**
     * 推入work
     * @param work landWork类型
     * @returns void
     */
    unshiftWork(work: landWork): void {
        let { duration, priority, expiration } = work.work
        if (!(priority || duration)) { throw new Error("error"); return; }

        work.shiftTime = getTime()
        if (this.queue.length == 0 || priority == 0 || priority < this.queue[0].work.priority) {
            this.queue.unshift(work)
            this.activate()
        } else if (expiration != 0) {
            /* 如果是0直接不推入（乐），不是0的话找一个好位置扔进去 */
            for (let insertIndex = 0; insertIndex < this.queue.length; insertIndex++) {
                if (priority > this.queue[insertIndex].work.priority) {
                    this.queue.splice(insertIndex + 1, 0, work)
                    break
                }
            }
        }

        this.timer || this.polling()
    }
    /**
     * 只负责往下走queue！
     */
    activate() {
        try {
            this.handleWork().then(() => {
                this.queue.length && this.activate();
            })
        } catch (e: any) {
            throw new Error(e)
        }
    }

    handleWork() {
        this.activeWork && this.activeWork.abort && (this.activeWork.abort())
        let tempPromise = []
        let mainPromise = new Promise(async (resolve) => {
            const TEMP_unshift = () => { this.queue.shift(); this.activeWork = null; resolve(1) }

            let active = this.queue[0]
            let { duration, handle, priority } = active.work
            //过期了
            if (!isNaN(active.work.expiration) && this.queue.length != 1 && priority != 0 && getTime() > active.shiftTime + active.work.expiration + 1000) {
                TEMP_unshift()
                return
            }
            let start = getTime()
            try { handle(); } catch { }
            let consume = getTime() - start
            if (consume < duration) {
                active.sleepTimer = this.sleep(duration - consume)
                tempPromise.push(active.sleepTimer)
                await active.sleepTimer
            }
            TEMP_unshift()
        })
        tempPromise.push(mainPromise)
        // sleep handle一起abort
        this.activeWork = abortWrapper(tempPromise)
        return this.activeWork
    }
    sleep(t: number) {
        return new Promise(res => setTimeout(res, t))
    }
    polling() {
        this.timer = this.queue.length
        this.queue.length && this.activate()
    }
}
function abortWrapper(p1: Array<promiseAbort & { abort?: Function }>): Promise<any> {
    let abort
    let p2 = new Promise((_, reject) => (abort = reject))
    let p: promiseAbort = Promise.race([...p1, p2])
    p.abort = abort
    return p
}
function getTime() {
    return new Date().getTime()
}
export { landWork, landCtrl }